package com.lagou.edu.mvcframework.servlet;

import com.lagou.edu.mvcframework.annocations.*;
import com.lagou.edu.mvcframework.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 陶肖寅
 * @version V1.0
 * @Package com.lagou.edu.mvcframework
 * @date 2020-02-01 08:49
 * @Copyright © 2020-2021 城云科技(中国)有限公司
 */
public class LgDispatcherServlet extends HttpServlet {
    private Properties properties = new Properties();
    private List<String> classnames = new ArrayList<>();
   private  Map<String,Object> beans = new HashMap<>();
    private List<Handler> handlers = new ArrayList<>();
    private static final String USERNAME="username";
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("等待执行get方法");
//       //根据uri获取到对应的方法，并执行
        Handler handler = getHander(req);
        if (handler==null){
            resp.getWriter().write("404 not found");
            return;
        }
        //进行权限拦截
        Boolean isFlag = doSecurity(req,handler);
        if (isFlag){
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().write("当前用户无访问权限");
            return;
        }
        //参数绑定
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        Object[] paraValues = new Object[parameterTypes.length];
        //像数组赋值
        //获取参数信息
        Map<String, String[]> parameterMap = req.getParameterMap();
        //填充普通参数
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            String value = StringUtils.join(param.getValue(), ",");//如同1，2
            if(!handler.getParamIndexMapping().containsKey(param.getKey().trim())){
                continue;
            }
            //如果存在，则放入值
            Integer index = handler.getParamIndexMapping().get(param.getKey().trim());//参数位置
            paraValues[index]=value; //将授权
        }
        int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        paraValues[requestIndex] = req;
        Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        paraValues[responseIndex] = resp;

        try {
            handler.getMethod().invoke(handler.getController(),paraValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 权限控制
     * @param handler
     * @return
     */
    private Boolean doSecurity(HttpServletRequest req,Handler handler) {

        //1.先进行方法判断过滤
        Boolean methodFlag =  doMethodSecurity(req,handler);
        if (methodFlag){
            return true;
        }
        //2.类进行方法判断
        return doClassSecurity(req,handler);
    }

    /**
     * 类过滤
     * @param req
     * @param handler
     * @return
     */
    private Boolean doClassSecurity(HttpServletRequest req, Handler handler) {
        //获取当前handler是否有有security标志
        Object object = handler.getController();
        if(!object.getClass().isAnnotationPresent(Security.class)){
            //如果执行方法不存在@Security 则不进行权限控制
            return Boolean.FALSE;
        }
        String[] values = object.getClass().getAnnotation(Security.class).value();
        if("".equals(values) && values.length < 1){
            //如果方法未设计value，则全部过滤
            return Boolean.TRUE;
        }
        //获取参数值
        String value = req.getParameter(USERNAME);
        if(!Arrays.asList(values).contains(value)){
            return false;
        }
        return Boolean.TRUE;
    }

    /**
     * 方法判断过滤
     * @param handler
     * @return
     */
    private Boolean doMethodSecurity(HttpServletRequest req,Handler handler) {
        Method method = handler.getMethod();
        if(!method.isAnnotationPresent(Security.class)){
            //如果执行方法不存在@Security 则不进行权限控制
            return Boolean.FALSE;
        }
        String[] values = method.getAnnotation(Security.class).value();
        if("".equals(values) && values.length < 1){
            //如果方法未设计value，则全部过滤
            return Boolean.TRUE;
        }
        //获取参数值
        String methodValues = req.getParameter(USERNAME);
        if(!Arrays.asList(values).contains(methodValues)){
            return false;
        }
        return Boolean.TRUE;
    }

    private Handler getHander(HttpServletRequest req) {
        System.out.println("handers====="+handlers.size());
        if (handlers.isEmpty()){
            return null;
        }
        //获取Uri地址
        String uri =  req.getRequestURI();
        System.out.println("uri==="+uri);
        for (Handler handler : handlers) {
            Matcher matcher = handler.getPattern().matcher(uri);
            if(!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("等待执行post方法");
        doPost(req,resp);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件 springMVC.properties
        String contextConfigLocation=config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        //包扫描
        doScan(properties.getProperty("scanPackage"));
        //初始化bean对象(实现ioc容器)
        doInstance();
        //4 实现依赖注入
        doAutowired();
        //构造一个HandlerMapping处理器映射器，将配置好的url与method建立连接
        initHandlerMapping();
        //等待讲求进入
        System.out.println("lagou mvc 初始化完成");
    }

    /**
     * 读取配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 基于classNames缓存的类全限定类名，以及反射技术
     * 初始化bean对象(实现ioc容器)
     */
    private void doInstance(){
        if (classnames.size() == 0){
            return;
        }
        classnames.forEach(classname ->{
            //将全限定命名反射成类
            try {
                Class<?>  clazz = Class.forName(classname);
                //区分Controller与service
                if (clazz.isAnnotationPresent(LgouController.class)){
                    //获取到类名如DemoController，将首字母小写放到ioc中
                    String simpleName = lowerFirst(clazz.getSimpleName());
                    beans.put(simpleName,clazz.newInstance());
                } else if (clazz.isAnnotationPresent(LgService.class)){
                    //判断value是否为空，如果为不则空，则使用values为key值
                    String simpleName = clazz.getAnnotation(LgService.class).value();
                    if (simpleName.trim()==null || "".equals(simpleName.trim())) {
                        simpleName = lowerFirst(clazz.getSimpleName());
                    }
                    beans.put(simpleName,clazz.newInstance());
                    //service往往是有接口的，然后以接口名在放一份
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        beans.put(anInterface.getName(),clazz.newInstance());
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        });
    }

    //首字母转换成小写
    public  String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if('A'<= chars[0] && chars[0]<= 'Z'){
            chars[0]+=32;
        }
        return String.valueOf(chars);
    }

    /**
     * 实现依赖注入
     */
    private void doAutowired() {
       if( beans.isEmpty()){
           return;
       }
        beans.forEach((beanId,bean) ->{
            //遍历所有bean属性，判断是否有LgAutowired对象，如果有则维护依赖注入
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            //遍历字段信息，查询依赖，
            for (Field declaredField : declaredFields) {
                // public IDemoService demoService;
                if(!declaredField.isAnnotationPresent(LgAutowired.class)){
                    return;
                }
                //如果存在autowired对象，则依赖处理,beanId;
                String beanName = declaredField.getAnnotation(LgAutowired.class).value();
                //如果value为空
                if ("".equals(beanName.trim())){
                    beanName = declaredField.getType().getName();
                }
                //开启注入
                declaredField.setAccessible(true);
                try {
                    declaredField.set(bean,beans.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        });

    }

    /**
     * 构造一个HandlerMapping处理器映射器，将配置好的url与method建立连接
     * 将Url与method建立关联
     */
    private void initHandlerMapping() {
        if (beans.isEmpty()){
            return;
        }
        beans.forEach((beansId,bean)->{
           Class<?> aClass =  bean.getClass();
           //排除controller以外的类
            if(!aClass.isAnnotationPresent(LgouController.class)){
                return;
            }
            String baseUrl="";
            if(aClass.isAnnotationPresent(LgRequestMapping.class)){
                 baseUrl =  aClass.getAnnotation(LgRequestMapping.class).value();//等同于/demo
            }
            //获取当前类中所有的方法
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {
                //遍历方法，判断是否有RequestMapping方法，如果有，则获取value的值
                if (!method.isAnnotationPresent(LgRequestMapping.class)){
                    //没有该注册则不处理
                    continue;
                }
                String methodUrl = method.getAnnotation(LgRequestMapping.class).value();//等同于/query
                String url = baseUrl+methodUrl;
                Handler handler = new Handler(method,bean, Pattern.compile(url));
                //计算参数位置
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i <parameters.length ; i++) {
                    Parameter parameter = parameters[i];
                    if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class){
                        handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),i);
                    }else{
                        //普通类型
                        handler.getParamIndexMapping().put(parameter.getName().trim(),i);
                    }
                }
                //建立Url与method的关系
               handlers.add(handler);
            }
        });

    }

    /**
     * 扫描类
     * @param scanPackage
     */
    private void doScan(String scanPackage) {
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
        File pack = new File(scanPackagePath);
        File[] files = pack.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                doScan(scanPackage + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                String className = scanPackage + "." + file.getName().replaceAll(".class", "");
                classnames.add(className);

            }
        }
    }


}
